package com.fplanning.planner.domain;

import java.util.List;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.drools.planner.api.domain.entity.PlanningEntity;
import org.drools.planner.api.domain.variable.PlanningVariable;
import org.drools.planner.api.domain.variable.ValueRange;
import org.drools.planner.api.domain.variable.ValueRangeType;

import com.fplanning.planner.business.TaxCalculator;
import com.fplanning.planner.solver.YearlyPlanDifficultyComparator;

@PlanningEntity(difficultyComparatorClass = YearlyPlanDifficultyComparator.class)
public class YearlyPlan
{
    int counter = 0;
    int id;

    int requiredAmount;

    int tax;

    List<FISlice> fiSlices;

    public int calculateTax()
    {
        tax = (int) TaxCalculator.calculateTax(this);
        return tax;
    }

    public YearlyPlan clone()
    {
        YearlyPlan clone = new YearlyPlan();
        clone.id = id;
        clone.tax = tax;
        clone.setFiSlices(fiSlices);
        clone.setRequiredAmount(requiredAmount);

        return clone;
    }

    public boolean solutionEquals(Object o)
    {
        if (this == o)
        {
            return true;
        }
        else if (o instanceof YearlyPlan)
        {
            YearlyPlan other = (YearlyPlan) o;
            return new EqualsBuilder().append(id, other.id)
                    .append(requiredAmount, other.requiredAmount)
                    .append(fiSlices, other.fiSlices).isEquals();
        }
        else
        {
            return false;
        }
    }

    /**
     * The normal methods {@link #equals(Object)} and {@link #hashCode()} cannot
     * be used because the rule engine already requires them (for performance in
     * their original state).
     * 
     * @see #solutionEquals(Object)
     */
    public int solutionHashCode()
    {
        return new HashCodeBuilder().append(id).append(requiredAmount)
                .append(fiSlices).toHashCode();
    }

    public final int getRequiredAmount()
    {
        return requiredAmount;
    }

    public final void setRequiredAmount(int requiredAmount)
    {
        this.requiredAmount = requiredAmount;
    }

    /**
     * @return the fiSlices
     */
    @PlanningVariable
    @ValueRange(type = ValueRangeType.FROM_SOLUTION_PROPERTY, solutionProperty = "fiSlicesList")
    public final List<FISlice> getFiSlices()
    {
        return fiSlices;
    }

    /**
     * @param fiSlices
     *            the fiSlices to set
     */
    public final void setFiSlices(List<FISlice> fiSlices)
    {
        this.fiSlices = fiSlices;
    }

    /**
     * @return the id
     */
    public final int getId()
    {
        return id;
    }

    /**
     * @param id
     *            the id to set
     */
    public final void setId(int id)
    {
        this.id = id;
    }

    public double calculateTotalFixed()
    {
        double result = 0;

        if (fiSlices != null)
        {
            for (FISlice fiSlice : fiSlices)
            {
                if (fiSlice != null && fiSlice.getfInstrument() != null
                        && fiSlice.getfInstrument().isTaxable())
                {
                    IFinancialInstrument instr = fiSlice.getfInstrument();
                    if (!instr.isAmountVariable())
                    {
                        result += instr.getAmount();
                    }
                }
            }
        }

        return result;
    }

    public double calculateTotalTaxable()
    {

        double result = 0;
        double totalAmount = getRequiredAmount() - calculateTotalFixed();

        if (fiSlices != null)
        {
            for (FISlice fiSlice : fiSlices)
            {
                if (fiSlice != null && fiSlice.getfInstrument() != null
                        && fiSlice.getfInstrument().isTaxable())
                {
                    IFinancialInstrument instr = fiSlice.getfInstrument();
                    if (instr.isAmountVariable())
                    {
                        double weight = fiSlice.getWeightPct();

                        int amount = (int) (weight * totalAmount / 100);
                        if (amount > instr.getAmount())
                        {
                            amount = instr.getAmount();
                        }
                        instr.setAmount(instr.getAmount() - amount);
                        result += amount;
                    }
                }
            }
        }

        return result + totalAmount;
    }

    @Override
    public String toString()
    {
        StringBuilder displayString = new StringBuilder();
        displayString.append("Year: " + id);
        displayString.append(' ');
        displayString.append("Required amount: " + getRequiredAmount());
        List<FISlice> slices = getFiSlices();
        double totalFixed = calculateTotalFixed();
        if (slices != null)
        {
            for (FISlice fiSlice : slices)
            {
                displayString.append("[" + fiSlice.getfInstrument() + ": "
                        + fiSlice.calculateAmount(requiredAmount - totalFixed)
                        + "]");
            }
        }
        displayString.append(' ');
        displayString.append(" tax: " + tax);

        return displayString.toString();
    }
}
